package com.doyens.gmall.gateway.filter;

import com.alibaba.fastjson.JSONObject;
import com.doyens.gmall.common.result.Result;
import com.doyens.gmall.common.result.ResultCodeEnum;
import com.doyens.gmall.common.util.IpUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.List;

@Slf4j
@Component
public class AuthGlobalFilter implements GlobalFilter {

    private AntPathMatcher antPathMatcher = new AntPathMatcher();
    @Autowired
    private RedisTemplate redisTemplate;

    @Value("${authUrls.url}")
    private String authUrls;

    //exchange 表示请求响应对象,可以获取请求响应相关信息，路径、参数、域的数据、响应数据
    //chain 过滤器调用链对象，放行方法
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //路径的拦截 所有包含inner的，都不让从html访问，inner仅限于微服务的远程调用
        // 所有包含admin的，是商家访问的(web-all项目没有访问admin的页面)
        ServerHttpRequest request = exchange.getRequest();
        //访问路径通过request对象获取  inner/a/b/c
        String path = request.getURI().getPath();
        //判断访问路径
        if (antPathMatcher.match("/**/inner/**", path)) {
            ServerHttpResponse response = exchange.getResponse();
            return out(response, ResultCodeEnum.PERMISSION);
        }

        String userId = getUserId(request);
        //判断路径中包含auth的异步请求，必须登录，响应json数据
        if (antPathMatcher.match("/api/**/auth/**", path)) {
            //判断是否登录,判断cookie中有没有令牌
            if (StringUtils.isEmpty(userId)) {
                ServerHttpResponse response = exchange.getResponse();
                return out(response, ResultCodeEnum.LOGIN_AUTH);
            }
        }

        //某些同步请求，必须登录的，跳转到登录页面
        String[] urls = authUrls.split(",");
        for (String url : urls) {
            if (path.indexOf(url) != -1) {
                if (StringUtils.isEmpty(userId)) {
                    //未登录,跳转到登录页面
                    //重定向： 302 location=新地址响应头
                    ServerHttpResponse response = exchange.getResponse();
                    response.setStatusCode(HttpStatus.SEE_OTHER);
                    response.getHeaders().set(HttpHeaders.LOCATION, "http://www.gmall.com/login.html?originUrl=" + request.getURI());
                    return response.setComplete();
                }


            }
        }


        String userTempId = getUserTempId(request);
        //如果登录了，有userId，把userId通过请求头，交给下一个资源
        //如果没登录，但是有临时用户id，携带临时用户id
        if (!StringUtils.isEmpty(userId) || !StringUtils.isEmpty(userTempId)) {
            if (!StringUtils.isEmpty(userId)) {
                //登录,有userId，顺带就把userId带过去
                request.mutate().header("userId", userId).build();
            }

            if (!StringUtils.isEmpty(userTempId)) {
                //判断是否有临时用户id
                request.mutate().header("userTempId", userTempId).build();
            }
        }
        // 将现在的request 变成 exchange对象
        return chain.filter(exchange.mutate().request(request).build());

        //兜底方法
//        return chain.filter(exchange);
    }


    /**
     * 获取临时用户的id，添加购物的时候，如果未登录，会生成一个临时用户id，保存未登录的购物车信息
     * 通过cookie携带了userTempId
     *
     * @param request
     * @return 临时用户的Id
     */
    private String getUserTempId(ServerHttpRequest request) {
        String userTempId = null;
        //从请求头中获取token
        List<String> list = request.getHeaders().get("userTempId");
        if (list != null && list.size() > 0) {
            userTempId = list.get(0);
            log.info("userTempId:" + userTempId);
        }
        //请求头中没有，从cookie中获取
        if (userTempId == null) {
            MultiValueMap<String, HttpCookie> cookies = request.getCookies();
            HttpCookie tokenCookie = cookies.getFirst("userTempId");
            if (tokenCookie != null) {
                userTempId = URLDecoder.decode(tokenCookie.getValue());
                log.info("userTempId:" + userTempId);
            }
        }
        return userTempId;
    }


    /**
     * 通过request获取cookie中的令牌 (token)
     * 通过令牌去redis中查询用户的id和ip
     * 当前的ip和redis中的ip不一致，被盗用令牌
     *
     * @param request 请求对象
     * @return 用户id
     */
    private String getUserId(ServerHttpRequest request) {
        String token = null;
        //从请求头中获取token
        List<String> list = request.getHeaders().get("token");
        if (list != null && list.size() > 0) {
            token = list.get(0);
            log.info("toke:" + token);
        }
        //请求头中没有，从cookie中获取
        if (token == null) {
            MultiValueMap<String, HttpCookie> cookies = request.getCookies();
            HttpCookie tokenCookie = cookies.getFirst("token");
            if (tokenCookie != null) {
                token = URLDecoder.decode(tokenCookie.getValue());
                log.info("token:" + token);
            }
        }

        if (!StringUtils.isEmpty(token)) {
            //从redis中获取userId
            String key = "user:login:" + token;
            log.info("key:" + key);
            Boolean hasKey = redisTemplate.hasKey(key);
            log.info("hasKey:" + hasKey);
            String json = (String) redisTemplate.opsForValue().get("user:login:" + token);
            log.info("json:" + json);
            if (!StringUtils.isEmpty(json)) {
                JSONObject jsonObject = JSONObject.parseObject(json, JSONObject.class);
                log.info("jsonObject:" + jsonObject);
                Long userId = jsonObject.getLong("userId");
                String ip = jsonObject.getString("ip");
                //判断登录的ip和当前访问的ip是否一致
                String ipNow = IpUtil.getGatwayIpAddress(request);
                if (ip.equals(ipNow)) {
                    return userId + "";
                } else {
                    return "-1";
                }

            }
        }
        //未登录
        return "";
    }

    // 接口鉴权失败返回数据
    //根据状态码和提示信息，响应result到浏览器
    private Mono<Void> out(ServerHttpResponse response, ResultCodeEnum resultCodeEnum) {
        // 返回用户没有权限登录
        Result<Object> result = Result.build(null, resultCodeEnum);
        byte[] bits = JSONObject.toJSONString(result).getBytes(StandardCharsets.UTF_8);
        DataBuffer wrap = response.bufferFactory().wrap(bits);
        response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        // 输入到页面
        return response.writeWith(Mono.just(wrap));
    }
}
